package cn.dansj.utils.tencent;

import cn.dansj.common.utils.json.DictMap;
import cn.dansj.common.utils.transfer.*;
import com.tencentcloudapi.common.AbstractModel;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.dnspod.v20210323.DnspodClient;
import com.tencentcloudapi.dnspod.v20210323.models.*;
import com.tencentcloudapi.domain.v20180808.DomainClient;
import com.tencentcloudapi.domain.v20180808.models.DescribeDomainNameListRequest;
import com.tencentcloudapi.domain.v20180808.models.DomainList;
import com.tencentcloudapi.lighthouse.v20200324.LighthouseClient;
import com.tencentcloudapi.lighthouse.v20200324.models.*;
import com.tencentcloudapi.ssl.v20191205.SslClient;
import com.tencentcloudapi.ssl.v20191205.models.*;
import com.tencentcloudapi.tat.v20201028.TatClient;
import com.tencentcloudapi.tat.v20201028.models.*;
import com.tencentcloudapi.tat.v20201028.models.Filter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class LightHouseUtils {
    private final String region;
    private volatile LighthouseClient lighthouseClient;
    private volatile DnspodClient dnspodClient;
    private volatile DomainClient domainClient;
    private volatile SslClient sslClient;
    private final Credential cred;
    private volatile TatClient tatClient;
    private static final ExecutorService threadPool = Executors.newCachedThreadPool();
    private final String md5;

    private static volatile OperationCallback operationCallback;

    public static void setOperationCallback(OperationCallback callback) {
        AssertUtils.state(operationCallback == null, "不允许重复初始化赋值");
        if (operationCallback == null) operationCallback = callback;
    }

    public static void callback(String primaryKey, String type, AbstractModel response) {
        if (operationCallback != null) operationCallback.callback(primaryKey, type, response);
    }

    public LightHouseUtils(String secretId, String secretKey, String region) {
        this.region = region;
        this.cred = new Credential(secretId, secretKey);
        md5 = Md5Utils.md5(secretId + "@" + secretKey + "@" + region);
    }

    public boolean checkMd5(String secretId, String secretKey, String region) {
        String md5 = Md5Utils.md5(secretId + "@" + secretKey + "@" + region);
        return md5.equals(this.md5);
    }

    private LighthouseClient getLighthouseClient() {
        if (lighthouseClient == null) {
            synchronized (LightHouseUtils.class) {
                if (lighthouseClient == null) {
                    lighthouseClient = new LighthouseClient(cred, region);
                }
            }
        }
        return lighthouseClient;
    }

    private DnspodClient getDnspodClient() {
        if (dnspodClient == null) {
            synchronized (LightHouseUtils.class) {
                if (dnspodClient == null) {
                    dnspodClient = new DnspodClient(cred, "");
                }
            }
        }
        return dnspodClient;
    }

    private TatClient getTatClient() {
        if (tatClient == null) {
            synchronized (LightHouseUtils.class) {
                if (tatClient == null) {
                    tatClient = new TatClient(cred, region);
                }
            }
        }
        return tatClient;
    }

    private SslClient getSslClient() {
        if (sslClient == null) {
            synchronized (LightHouseUtils.class) {
                if (sslClient == null) {
                    sslClient = new SslClient(cred, "");
                }
            }
        }
        return sslClient;
    }

    private DomainClient getDomainClient() {
        if (domainClient == null) {
            synchronized (LightHouseUtils.class) {
                if (domainClient == null) {
                    domainClient = new DomainClient(cred, "");
                }
            }
        }
        return domainClient;
    }

    private DictMap getExceptionDictMap(Exception exception) {
        if (exception instanceof TencentCloudSDKException tencentCloudSDKException) {
            return DictMap.of("responseStatus", false, "message", tencentCloudSDKException.getMessage(), "errorCode", tencentCloudSDKException.getErrorCode(), "requestId", tencentCloudSDKException.getRequestId());
        } else {
            return DictMap.of("responseStatus", false, "message", exception.getMessage());
        }
    }

    private static <O extends AbstractModel> DictMap transferResponse(O resp) {
        return DictMap.from(AbstractModel.toJsonString(resp)).put("responseStatus", true);
    }

    public DictMap describeFirewallRules(String instanceId) {
        try {
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DescribeFirewallRulesRequest req = new DescribeFirewallRulesRequest();
            req.setInstanceId(instanceId);
            req.setOffset(0L);
            req.setLimit(100L);
            // 返回的resp是一个DescribeFirewallRulesResponse的实例，与请求对象对应
            return transferResponse(getLighthouseClient().DescribeFirewallRules(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap createFirewallRules(String instanceId, List<FirewallRule> firewallRule) {
        try {
            // 实例化一个请求对象,每个接口都会对应一个request对象
            CreateFirewallRulesRequest req = new CreateFirewallRulesRequest();
            req.setInstanceId(instanceId);
            req.setFirewallRules(firewallRule.toArray(new FirewallRule[]{}));
            // 返回的resp是一个DescribeFirewallRulesResponse的实例，与请求对象对应
            return transferResponse(getLighthouseClient().CreateFirewallRules(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap createFirewallRules(String instanceId, FirewallRule firewallRule) {
        return createFirewallRules(instanceId, ArrayUtils.asList(firewallRule));
    }

    public DictMap deleteFirewallRules(String instanceId, List<FirewallRule> firewallRules) {
        try {
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DeleteFirewallRulesRequest req = new DeleteFirewallRulesRequest();
            req.setInstanceId(instanceId);
            req.setFirewallRules(firewallRules.toArray(new FirewallRule[]{}));
            // 返回的resp是一个DescribeFirewallRulesResponse的实例，与请求对象对应
            return transferResponse(getLighthouseClient().DeleteFirewallRules(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap deleteFirewallRules(String instanceId, FirewallRule rule) {
        return deleteFirewallRules(instanceId, ArrayUtils.asList(rule));
    }

    public DictMap modifyFirewallRules(String instanceId, List<FirewallRule> rules) {
        try {
            // 实例化一个请求对象,每个接口都会对应一个request对象
            ModifyFirewallRulesRequest req = new ModifyFirewallRulesRequest();
            req.setInstanceId(instanceId);
            req.setFirewallRules(rules.toArray(new FirewallRule[]{}));
            // 返回的resp是一个DescribeFirewallRulesResponse的实例，与请求对象对应
            return transferResponse(getLighthouseClient().ModifyFirewallRules(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap describeInstances() {
        try {
            return transferResponse(getLighthouseClient().DescribeInstances(new DescribeInstancesRequest()));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap describeInstanceVncUrl(String instanceId) {
        try {
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DescribeInstanceVncUrlRequest req = new DescribeInstanceVncUrlRequest();
            req.setInstanceId(instanceId);
            // 返回的resp是一个DescribeInstanceVncUrlResponse的实例，与请求对象对应
            return transferResponse(getLighthouseClient().DescribeInstanceVncUrl(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap describeNameDomainList() {
        try {
            return transferResponse(getDomainClient().DescribeDomainNameList(new DescribeDomainNameListRequest()));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap describeRecordList(String domain) {
        try {
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DescribeRecordListRequest req = new DescribeRecordListRequest();
            req.setDomain(domain);
            return transferResponse(getDnspodClient().DescribeRecordList(req));
        } catch (Exception e) {
            return getExceptionDictMap(e);
        }
    }

    public DictMap describeDomainList() {
        try {
            return transferResponse(getDnspodClient().DescribeDomainList(new DescribeDomainListRequest()));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap describeDomain() {
        final Future<DictMap> describeNameDomainList = threadPool.submit(this::describeNameDomainList);
        final Future<DictMap> describeDomainList = threadPool.submit(this::describeDomainList);
        try {
            final DictMap nameDomainMap = describeNameDomainList.get();
            if (!nameDomainMap.getBooleanValue("responseStatus")) {
                return DictMap.of("responseStatus", false, "message", nameDomainMap.getString("message"));
            }

            final DictMap domainMap = describeDomainList.get();
            if (!domainMap.getBooleanValue("responseStatus")) {
                return DictMap.of("responseStatus", false, "message", domainMap.getString("message"));
            }

            List<DictMap> domains = new ArrayList<>();
            for (DomainListItem domainListItem : domainMap.getList("DomainList", DomainListItem.class)) {
                for (DomainList domainList : nameDomainMap.getList("DomainSet", DomainList.class)) {
                    DictMap domainListItemMap = DictMap.from(domainListItem);
                    DictMap domainListMap = DictMap.from(domainList);
                    if (domainListItemMap.getString("name").equals(domainListMap.getString("domainName"))) {
                        domainListMap.putAll(domainListItemMap);
                        domains.add(domainListMap);
                    }
                }
            }
            domainMap.put("DomainList", domains);
            return domainMap;
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap deleteRecord(String domain, long recordId) {
        try {
            DeleteRecordRequest req = new DeleteRecordRequest();
            req.setRecordId(recordId);
            req.setDomain(domain);
            return transferResponse(getDnspodClient().DeleteRecord(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap createRecord(CreateRecordRequest createRecordRequest) {
        try {
            return transferResponse(getDnspodClient().CreateRecord(createRecordRequest));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap modifyRecord(ModifyRecordRequest modifyRecordRequest) {
        try {
            return transferResponse(getDnspodClient().ModifyRecord(modifyRecordRequest));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap describeRecordType(String grade) {
        try {
            DescribeRecordTypeRequest req = new DescribeRecordTypeRequest();
            req.setDomainGrade(grade);
            return transferResponse(getDnspodClient().DescribeRecordType(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    /**
     * 获取等级允许的线路
     *
     * @param domain 域名
     * @param grade  等级
     */
    public DictMap describeRecordLineList(String domain, String grade) {
        try {
            DescribeRecordLineListRequest req = new DescribeRecordLineListRequest();
            req.setDomain(domain);
            req.setDomainGrade(grade);
            return transferResponse(getDnspodClient().DescribeRecordLineList(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap modifyRecordStatus(String domain, long recordId, String status) {
        try {
            ModifyRecordStatusRequest req = new ModifyRecordStatusRequest();
            req.setDomain(domain);
            req.setRecordId(recordId);
            req.setStatus(status);
            return transferResponse(getDnspodClient().ModifyRecordStatus(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap describeCertificates() {
        try {
            final DescribeCertificatesRequest describeCertificatesRequest = new DescribeCertificatesRequest();
            describeCertificatesRequest.setLimit(100L);
            return transferResponse(getSslClient().DescribeCertificates(describeCertificatesRequest));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap describeCertificateDetail(String CertificateId) {
        try {
            DescribeCertificateDetailRequest req = new DescribeCertificateDetailRequest();
            req.setCertificateId(CertificateId);
            return transferResponse(getSslClient().DescribeCertificateDetail(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap createCertificate(ApplyCertificateRequest certificateRequest) {
        try {
            return transferResponse(getSslClient().ApplyCertificate(certificateRequest));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public void batchCreateCertificate(ApplyCertificateRequest certificateRequest) {
        final String domainName = certificateRequest.getDomainName();
        for (String domain : domainName.split("\n")) {
            final ApplyCertificateRequest newCertificateRequest = new ApplyCertificateRequest(certificateRequest);
            newCertificateRequest.setDomainName(domain);
            createCertificate(newCertificateRequest);
        }
    }

    public DictMap cancelAuditCertificate(String certificateId) {
        try {
            CancelAuditCertificateRequest req = new CancelAuditCertificateRequest();
            req.setCertificateId(certificateId);
            return transferResponse(getSslClient().CancelAuditCertificate(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap checkCertificateDomainVerification(String certificateId) {
        try {
            CheckCertificateDomainVerificationRequest req = new CheckCertificateDomainVerificationRequest();
            req.setCertificateId(certificateId);
            return transferResponse(getSslClient().CheckCertificateDomainVerification(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap deleteCertificate(String certificateId) {
        try {
            DeleteCertificateRequest req = new DeleteCertificateRequest();
            req.setCertificateId(certificateId);
            final DeleteCertificateResponse resp = getSslClient().DeleteCertificate(req);
            callback("certificateId@" + certificateId, "deleteCertificate", resp);
            return transferResponse(resp);
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public int batchDeleteCertificate(List<String> certificateIds) {
        int success = 0;
        for (String certificateId : certificateIds) {
            final DictMap dictMap = deleteCertificate(certificateId);
            if (dictMap.getBooleanValue("DeleteResult", false)) success += 1;
        }
        return success;
    }

    public DictMap revokeCertificate(String certificateId) {
        try {
            RevokeCertificateRequest req = new RevokeCertificateRequest();
            req.setCertificateId(certificateId);
            final RevokeCertificateResponse resp = getSslClient().RevokeCertificate(req);
            callback("certificateId@" + certificateId, "revokeCertificate", resp);
            return transferResponse(resp);
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public void batchRevokeCertificate(List<String> certificateIds) {
        for (String certificateId : certificateIds) {
            revokeCertificate(certificateId);
        }
    }

    public DictMap describeDownloadCertificateUrl(String certificateId, String serviceType) {
        try {
            DescribeDownloadCertificateUrlRequest req = new DescribeDownloadCertificateUrlRequest();
            req.setCertificateId(certificateId);
            req.setServiceType(serviceType);
            return transferResponse(getSslClient().DescribeDownloadCertificateUrl(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap runCommand(String content, String... instanceId) {
        try {
            // 实例化一个请求对象,每个接口都会对应一个request对象
            RunCommandRequest req = new RunCommandRequest();
            req.setContent(content);
            req.setInstanceIds(instanceId);
            req.setCommandType("SHELL");
            req.setSaveCommand(false);
            return transferResponse(getTatClient().RunCommand(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public boolean deployCertificate(String instanceId, String certificateId, String serviceType, String dataDir, String targetDir, boolean removeZip, String extraCmd) throws InterruptedException {
        final DictMap data = describeDownloadCertificateUrl(certificateId, serviceType);
        final String url = data.getString("DownloadCertificateUrl");
        AssertUtils.notNull(url, "证书下载链接获取失败");
        final String name = data.getString("DownloadFilename");
        final String cmdMkdirDataMenu = "mkdir -p " + dataDir;
        final String cmdDownload = "curl -o " + dataDir + "/" + name + " " + url.replace("&", "%26");
        final String cmdUnzip = "unzip -o -q -d " + targetDir + " -j " + dataDir + "/" + name + " '*.key' '*.pem'";
        final String cmdRmZip = removeZip ? "rm -rf " + dataDir + "/" + name : null;
        final String deployCmd = String.join(" && ", ArrayUtils.asList(cmdMkdirDataMenu, cmdDownload, cmdUnzip, cmdRmZip, extraCmd).stream().filter(Verification::checkNotNull).toList());
        final String cmdBase64 = Transformation.encodeBase64(deployCmd);
        final DictMap dictMap = runCommand(cmdBase64, instanceId);
        String commandId = dictMap.getString("CommandId");
        if (Verification.checkNull(commandId)) {
            return false;
        }

        int commandStatus = getCommandStatus(commandId);
        int loopTimes = 5;
        while (commandStatus == 0 && loopTimes > 0) {
            Thread.sleep(2000);
            loopTimes--;
            commandStatus = getCommandStatus(commandId);
        }
        if (commandStatus == 0) {
            String invocationId = dictMap.getString("InvocationId");
            cancelInvocation(invocationId);
        }

        return Transformation.castToBoolean(Transformation.lambdaIf(commandStatus == 1, true, false));
    }

    private static final DictMap taskStatus = DictMap.of("PENDING", 0, "DELIVERING", 0, "DELIVER_DELAYED", 0, "DELIVER_FAILED", 2, "START_FAILED", 2, "RUNNING", 0, "SUCCESS", 1, "FAILED", 2, "TIMEOUT", 2, "TASK_TIMEOUT", 2, "CANCELLING", 0, "CANCELLED", 2, "TERMINATED", 2);

    public int getCommandStatus(String commandId) {
        try {
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DescribeInvocationTasksRequest req = new DescribeInvocationTasksRequest();
            Filter[] filters1 = new Filter[1];
            Filter filter1 = new Filter();
            filter1.setName("command-id");
            filter1.setValues(new String[]{commandId});
            filters1[0] = filter1;
            req.setFilters(filters1);
            final DescribeInvocationTasksResponse resp = getTatClient().DescribeInvocationTasks(req);
            final List<Integer> status = Arrays.stream(resp.getInvocationTaskSet()).map(e -> taskStatus.getIntValue(e.getTaskStatus())).toList();
            int taskStatus;
            if (status.contains(0)) {
                taskStatus = 0;
            } else if (status.contains(2)) {
                taskStatus = 2;
            } else {
                taskStatus = 1;
            }

            return taskStatus;
        } catch (Exception exception) {
            return 2;
        }
    }

    public DictMap cancelInvocation(String invocationId) {
        try {
            CancelInvocationRequest req = new CancelInvocationRequest();
            req.setInvocationId(invocationId);
            return transferResponse(getTatClient().CancelInvocation(req));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }

    public DictMap describeCertificateOperateLogs() {
        try {
            final DescribeCertificateOperateLogsRequest describeCertificateOperateLogsRequest = new DescribeCertificateOperateLogsRequest();
            describeCertificateOperateLogsRequest.setLimit(50L);
            describeCertificateOperateLogsRequest.setStartTime(DateTime.getInstance().trunc().addDay(-365).toString("yyyy-MM-dd HH:mm:ss"));
            return transferResponse(getSslClient().DescribeCertificateOperateLogs(describeCertificateOperateLogsRequest));
        } catch (Exception exception) {
            return getExceptionDictMap(exception);
        }
    }
}
